/**
 * Licensed under the Apache License Version 2.0.
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package net.interknit.knitter.cg.view;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import net.interknit.knitter.cg.CGMain;
import net.interknit.knitter.cg.cfg.CGConfig;
import net.interknit.knitter.cg.cfg.CGConfig.RequestParamType;
import net.interknit.knitter.cg.layout.LayoutElement;
import net.interknit.knitter.cg.layout.TargetElement;
import net.interknit.knitter.cg.model.JavaMethod;
import net.interknit.knitter.cg.model.JavaType;
import net.interknit.knitter.cg.template.ParameterBeanTemplateHolder;
import net.interknit.knitter.cg.util.ReflectionHelper;

/**
 *
 */
public class SeedTypeView extends CustomTypeView
{
	public SeedTypeView(JavaType javaType)
	{
		super(javaType);

		SeedTypeHolder.getInstance().setSeedTypeView(this);
	}

	@Override
	protected String[] getAdditionalPlaceholders()
	{
		String[] arr = super.getAdditionalPlaceholders();

		List<String> list = new ArrayList<String>();

		list.addAll(arr != null ? Arrays.asList(arr) : new ArrayList<String>());
		list.add("SEED_CONTEXT");
		list.add("BASE_ADDRESS_XML");
		list.add("GENERATED_CLASS_CSV");
		list.add("METHODS_XML");

		return list.toArray(new String[list.size()]);
	}

	@Override
	public LayoutElement getLayoutElement()
	{
		return new TargetElement(this);
	}

	@Override
	public String COMMENTS()
	{
		String comments = CGMain.getInstance().getCGConfig().getTargetComments(
				ReflectionHelper.getClassName(getJavaType().getTypeClass()));

		if(comments != null && ! "".equals(comments))
			return comments;
		else
			return super.COMMENTS();
	}

	public String SEED_CONTEXT()
	{
		String adapterName = CGMain.getInstance().getCGConfig().getRestName(ReflectionHelper.getClassName(getJavaType().getTypeClass()));
		return adapterName != null ? adapterName : getJavaType().getJavaTypeId();
	}

	public String BASE_ADDRESS_XML()
	{
		return SEED_CONTEXT();
	}

	public String GENERATED_CLASS_CSV()
	{
		return "java.lang.reflect.Method.class,java.lang.Class.class";
	}

	public String METHODS_XML()
	{
		StringBuilder sb = new StringBuilder();

		sb.append("<methods>");

		for(JavaMethod javaMethod : getJavaType().getJavaMethods())
		{
			String xmlName = null;
			String xmlPath = null;
			String xmlType = null;
			String xmlConsumes = null;
			String xmlProduces = null;
			String xmlParamnames = null;
			String xmlIn = null;
			String xmlOut = null;

			String methodName = javaMethod.getName();
			String methodId = javaMethod.getId();
			JavaType methodReturnJavaType = javaMethod.getReturnJavaType();
			List<JavaType> methodParameterJavaTypes = javaMethod.getParameterJavaTypes();

			RequestParamType requestParamType = RequestParamType.PATH;

			boolean paramConsolidationRequired = false;
			boolean inputStreamReturnType = false;
			boolean readerReturnType = false;

			xmlName = methodName;

			// if this is not a getter/setter method (i.e., this is a service method)
			CGConfig cfg = CGMain.getInstance().getCGConfig();

			String targetClassName =
				ReflectionHelper.getClassName(javaMethod.getTypeClass());
			String[] parameterClassNames =
				ReflectionHelper.getDisplayClassNames(javaMethod.getMethod().getParameterTypes());

			//
			// construct xmlConsumes value if specified in the configuration.
			// otherwise, leave it (will default to ("*/*"))
			//
			xmlConsumes = cfg.getMethodConsumes(
					targetClassName, methodName, parameterClassNames);

			if(xmlConsumes != null && ! "".equals(xmlConsumes) && ! "null".equals(xmlConsumes))
			{
				xmlConsumes = xmlConsumes.trim();
			}

			//
			// construct xmlProduces value if specified in the configuration.
			// otherwise, leave it (will default to "*/*"))
			//
			xmlProduces = cfg.getMethodProduces(
					targetClassName, methodName, parameterClassNames);

			if(xmlProduces != null && ! "".equals(xmlProduces) && ! "null".equals(xmlProduces))
			{
				xmlProduces = xmlProduces.trim();
			}
			else if(
					methodReturnJavaType.getInspector().isFileType() ||
					methodReturnJavaType.getInspector().isInputStreamType() ||
					methodReturnJavaType.getInspector().isReaderType())
			{
				/**
				 * @see javax.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM
				 */
				xmlProduces = "application/octet-stream";
			}

			//
			// find requestParamType value from the configuration
			//
			requestParamType = cfg.getMethodRequestParamType(
					targetClassName, methodName, parameterClassNames);

			inputStreamReturnType =
					methodReturnJavaType.getInspector().isFileType() ||
					methodReturnJavaType.getInspector().isInputStreamType();

			readerReturnType =
					methodReturnJavaType.getInspector().isReaderType();

			//
			// find xmlParamnames value from the configuration
			//
			String[] paramNames = cfg.getMethodParameterName(
					targetClassName, methodName, parameterClassNames);

			for(int i = 0; paramNames != null && i < paramNames.length; i++)
			{
				if(xmlParamnames == null) xmlParamnames = "";

				if(i > 0) xmlParamnames += ",";
				xmlParamnames += paramNames[i];
			}

			//
			// find the REST method if specified in the configuration
			//
			String restMethod = cfg.getMethodRestMethod(
					targetClassName, methodName, parameterClassNames);

			if(restMethod != null) xmlType = restMethod;
			else if(methodParameterJavaTypes.isEmpty()) xmlType = "GET";
			else xmlType = "POST";

			//
			// find the REST name if specified in the configuration
			//
			String restName = cfg.getMethodRestName(
					targetClassName, methodName, parameterClassNames);

			//
			// use method's ID as the resource path base (e.g., 'signIn' or
			// 'signIn39' - for overloaded methods) if configuration does
			// not have a specific rest-name for this method. otherwise,
			// use the specified name if found.
			//
			xmlPath = restName != null ? restName : methodId;

			//
			// method parameters need to be consolidated to a single bean object
			// if any one of the parameter is of a non-basic (text-friendly) type
			//

			// count number of basic type and non basic types from the list of method
			// parameter types

			int numOfTotalTypes = methodParameterJavaTypes.size();
			int numOfNonBasicTypes = 0;

			for(int i = 0; i < methodParameterJavaTypes.size(); i++)
			{
				JavaType parameterJavaType = methodParameterJavaTypes.get(i);

				if(! parameterJavaType.getInspector().isBasicType())
					numOfNonBasicTypes++;
			}

			// if number of non-basic types exceeds 1 where the total parameter count exceeds 1,
			// parameter consolidation is reuired
			paramConsolidationRequired = numOfTotalTypes > 1 && numOfNonBasicTypes > 1;

			String paramTypeValue = null;

			if(inputStreamReturnType) paramTypeValue = "byteStream";
			else if(readerReturnType) paramTypeValue = "charStream";
			else paramTypeValue = JavaTypeViewFactory.create(methodReturnJavaType).ADAPTER_NAME();

			paramTypeValue = ReflectionHelper.getStandardTypeName(paramTypeValue);

			StringBuilder xmlOutBuilder = new StringBuilder();
			xmlOutBuilder.append("<out>").append("<parameter type=\"" + paramTypeValue + "\" /></out>");
			xmlOut = xmlOutBuilder.toString();

			StringBuilder xmlInBuilder = new StringBuilder();
			if(! paramConsolidationRequired)
			{
				xmlInBuilder.append("<in>");
				for(int i = 0; i < methodParameterJavaTypes.size(); i++)
				{
					JavaType pjt = methodParameterJavaTypes.get(i);
					String pjtName = ReflectionHelper.getStandardTypeName(
							JavaTypeViewFactory.create(pjt).ADAPTER_NAME());

					xmlInBuilder.append("<parameter type=\"" + pjtName + "\" />");
				}
				xmlInBuilder.append("</in>");
			}
			else
			{
				xmlInBuilder.append("<in>");
				xmlInBuilder.append("<parameter type=\"" +
						ParameterBeanTemplateHolder.getInstance().get(
								javaMethod.getJavaType().getJavaTypeId(false) + "_" + methodId + "Args")
								.ADAPTER_NAME() + "\" /></in>");
			}
			xmlIn = xmlInBuilder.toString();

			sb.append("<method " +
					(xmlName != null ? "name=\"" + xmlName + "\" " : "") +
					(xmlPath != null ? "path=\"" + xmlPath + "\" " : "") +
					(xmlType != null ? "type=\"" + xmlType + "\" " : "") +
					(xmlConsumes != null ? "consumes=\"" + xmlConsumes + "\" " : "") +
					(xmlProduces != null ? "produces=\"" + xmlProduces + "\" " : "") +
					"paramstyle=\"" + requestParamType.name() + "\" " +
					"useasqueryparams=\"" + String.valueOf(requestParamType.equals(RequestParamType.QUERY)) + "\" " +
					(xmlParamnames != null ? "paramnames=\"" + xmlParamnames + "\" " : "") +
					">");
			sb.append(xmlIn);
			sb.append(xmlOut);
			sb.append("</method>");
		}
		sb.append("</methods>");

		return sb.toString();
	}
}
